home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.12 Dec 94 / ThreadedSprocket / Lib / Futures.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-17  |  7.1 KB  |  269 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        Futures.c
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Written by:    xxx put writers here xxx
  7.  
  8.     Copyright:    © 1993 by Apple Computer, Inc., all rights reserved.
  9.                 © 1993-1994 by Steve Sisak, all rights reserved.
  10.  
  11.     Change History (most recent first):
  12.  
  13.          <1>    10/14/93    bsp        first checked in
  14.  
  15. */
  16.  
  17. #include "Futures.h"
  18. #include <Threads.h>
  19. #include <GestaltEqu.h>
  20.  
  21. pascal OSErr    IsFutureBlock(AppleEvent* message);
  22. pascal OSErr    DoThreadBlock(AppleEvent* message);
  23. pascal OSErr    DoThreadUnblock(AppleEvent* message);
  24.  
  25. #define kAERefconAttribute            'refc'
  26. #define kAENonexistantAttribute        'gag!'
  27. #define kImmediateTimeout            0
  28.  
  29. struct ThreadList
  30. {
  31.     short        numThreads;
  32.     ThreadID    threads[1];
  33. };
  34.  
  35. typedef struct ThreadList ThreadList, *ThreadListPtr, **ThreadListHdl;
  36.  
  37. #define sizeofThreadList(numthreads) (sizeof(ThreadList) + ((numthreads)-1)*sizeof(ThreadID))
  38.  
  39. typedef pascal OSErr (*AESpecialHandlerProcPtr)(AppleEvent *theAppleEvent);
  40.  
  41. enum {
  42.     uppAESpecialHandlerProcInfo = kPascalStackBased
  43.          | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  44.          | STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(AppleEvent*)))
  45. };
  46.  
  47. #if USESROUTINEDESCRIPTORS
  48. typedef UniversalProcPtr AESpecialHandlerUPP;
  49.  
  50. #define CallAESpecialHandlerProc(userRoutine, theAppleEvent, reply, handlerRefcon)        \
  51.         CallUniversalProc((UniversalProcPtr)(userRoutine), uppAESpecialHandlerProcInfo, (theAppleEvent), (reply), (handlerRefcon))
  52. #define NewAESpecialHandlerProc(userRoutine)        \
  53.         (AESpecialHandlerUPP) NewRoutineDescriptor((ProcPtr)(userRoutine), uppAESpecialHandlerProcInfo, GetCurrentISA())
  54. #else
  55. //typedef AESpecialHandlerProcPtr AESpecialHandlerUPP;
  56. typedef ProcPtr AESpecialHandlerUPP;
  57.  
  58. #define CallAESpecialHandlerProc(userRoutine, theAppleEvent, reply, handlerRefcon)        \
  59.         (*(userRoutine))((theAppleEvent), (reply), (handlerRefcon))
  60. #define NewAESpecialHandlerProc(userRoutine)        \
  61.         (AESpecialHandlerUPP)(userRoutine)
  62. #endif
  63.  
  64. AESpecialHandlerUPP gDoThreadBlockUPP    = nil;
  65. AESpecialHandlerUPP gDoThreadUnblockUPP    = nil;
  66. AESpecialHandlerUPP gIsFutureBlockUPP    = nil;
  67.  
  68.  
  69. pascal OSErr InitFutures(void)
  70. {
  71.     OSErr    err;
  72.     long    aResponse;
  73.     
  74.     gDoThreadBlockUPP    = NewAESpecialHandlerProc(DoThreadBlock);
  75.     gDoThreadUnblockUPP    = NewAESpecialHandlerProc(DoThreadUnblock);
  76.     gIsFutureBlockUPP    = NewAESpecialHandlerProc(DoThreadBlock);
  77.     
  78.     err = Gestalt(gestaltThreadMgrAttr, &aResponse);
  79.     
  80.     if (err == noErr)
  81.         err = AEInstallSpecialHandler('blck', gDoThreadBlockUPP, false);
  82.     if (err == noErr)
  83.         err = AEInstallSpecialHandler('unbk', gDoThreadUnblockUPP, false);
  84.  
  85.     return err;
  86. }
  87.  
  88. pascal OSErr DoThreadBlock(AppleEvent* message)
  89. {
  90.     OSErr            err;
  91.     DescType        actualType;
  92.     Size            actualSize;
  93.     ThreadListHdl    threadList;
  94.     ThreadID        currentThread;
  95.     
  96.     //    Apparently the current thread needs to access some information, which 
  97.     //    is really a future.  We need to see if there is already a list of threads
  98.     //    blocked on this message.  If there isn't, create an empty list.  Add
  99.     //    the current thread to the list.  Sleep the current thread.
  100.  
  101.  
  102.     err = GetCurrentThread(¤tThread);
  103.  
  104.     if (err == noErr)
  105.     {
  106.         err = AEGetAttributePtr(message, kAERefconAttribute, typeLongInteger, &actualType,
  107.                                 (Ptr) &threadList, sizeof(threadList), &actualSize);
  108.  
  109.         if (err == errAEDescNotFound || err == noErr && !threadList)
  110.         {
  111.             // If we can't find a waiting thread list, then create one containing 
  112.             // just ourself and put it back in the message
  113.         
  114.             threadList = (ThreadListHdl) NewHandle(sizeofThreadList(1));
  115.             
  116.             err = MemError();
  117.             
  118.             if (err == noErr)
  119.             {
  120.                 (**threadList).numThreads = 1;
  121.                 (**threadList).threads[0] = currentThread;
  122.         
  123.                 err = AEPutAttributePtr(message, kAERefconAttribute, typeLongInteger,
  124.                                         (Ptr) &threadList, sizeof(threadList));
  125.             }
  126.         }
  127.         else if (err == noErr)
  128.         {
  129.             // Otherwise just append ourself onto the existing list
  130.         
  131.             short numWaiting = (**threadList).numThreads;
  132.         
  133.             SetHandleSize((Handle) threadList, sizeofThreadList(numWaiting+1));
  134.     
  135.             err = MemError();
  136.             
  137.             if (err == noErr)
  138.             {
  139.                 (**threadList).threads[numWaiting] = currentThread;
  140.                 (**threadList).numThreads = ++numWaiting;
  141.             }
  142.         }
  143.     }
  144.     
  145.     // If there was an error don't block
  146.  
  147.     if (err == noErr)
  148.         err = SetThreadState(currentThread, kStoppedThreadState, kNoThreadID);
  149.  
  150.     return err;
  151. }
  152.  
  153. pascal OSErr DoThreadUnblock(AppleEvent* message)
  154. {
  155.     OSErr                    err;
  156.     DescType                actualType;
  157.     Size                    actualSize;
  158.     ThreadState                blockedThreadState;
  159.     ThreadListHdl            threadList;
  160.  
  161.     //    This message has just turned real.  If there is a list of threads blocked 
  162.     //    because they tried to access the data, walk through the list of blocked
  163.     //    threads waking and deallocating the list element as you go.
  164.  
  165.     err = AEGetAttributePtr(message, kAERefconAttribute, typeLongInteger, &actualType,
  166.                             (Ptr) &threadList, sizeof(threadList), &actualSize);
  167.     
  168.     if (err == errAEDescNotFound)
  169.     {
  170.         //    It's possible that this unblocking handler will get called for ALL replies
  171.         //    to apple events, not just futures.  If that's the case, then getting
  172.         //    the above error is not really an error.  Clear it and just return.
  173.     
  174.         err = noErr;
  175.     }
  176.     else if (err == noErr && threadList)
  177.     {
  178.         //    If there's a waiting list, make all threads in it ready for execution
  179.         //    We won't report errors inside the loop because:
  180.         //        1)    one of the threads might have been disposed of
  181.         //        2)    there's nothing we can do to recover at this point
  182.         //        3)    the important thing is to wake up all remaining threads.
  183.  
  184.         ThreadID*    nextThread = (**threadList).threads;
  185.         short          numWaiting = (**threadList).numThreads;
  186.         
  187.         while (--numWaiting >= 0)
  188.         {
  189.             ThreadID blockedThread = *nextThread++;
  190.             OSErr     err1 = GetThreadState(blockedThread, &blockedThreadState);
  191.             
  192.             if (err1 == noErr && blockedThreadState == kStoppedThreadState)
  193.             {
  194.                 err1 = SetThreadState(blockedThread, kReadyThreadState, kNoThreadID);
  195.             }
  196.         }
  197.         
  198.         // Now free the list handle (and take it out of the refCon just to be safe)
  199.                     
  200.         DisposeHandle((Handle) threadList);
  201.         
  202.         threadList = nil;
  203.                     
  204.         err = AEPutAttributePtr(message, kAERefconAttribute, typeLongInteger,
  205.                                 (Ptr) &threadList, sizeof(threadList));
  206.  
  207.     }    
  208.  
  209.     return err;
  210. }
  211.  
  212. pascal OSErr BlockUntilReal(AppleEvent* message)
  213. {
  214.     OSErr            err;
  215.     AERecord        nonExistentParameter;
  216.     
  217.     err = AEGetParamDesc(message, kAENonexistantAttribute,
  218.                         typeAERecord, &nonExistentParameter);
  219.  
  220.     if (err == errAEDescNotFound)
  221.         err = noErr;
  222.     
  223.     return err;
  224. }
  225.  
  226. pascal Boolean IsFuture(AppleEvent* message)
  227. {
  228.     OSErr                err;
  229.     AESpecialHandlerUPP    oldBlockingProc;
  230.     Boolean                isFuture = false;
  231.     
  232.     err = AEGetSpecialHandler('blck', &oldBlockingProc, false);
  233.     
  234.     if (err == noErr)
  235.     {
  236.         err = AEInstallSpecialHandler('blck', gIsFutureBlockUPP, false);
  237.     
  238.         if (err == noErr)
  239.         {
  240.             if (BlockUntilReal(message) == errAEReplyNotArrived)
  241.             {
  242.                 isFuture = true;
  243.             }        
  244.             
  245.             err = AEInstallSpecialHandler('blck', oldBlockingProc, false);
  246.         }
  247.     }
  248.     
  249.     return isFuture;
  250. }
  251.  
  252. pascal OSErr IsFutureBlock(AppleEvent* /*message*/)
  253. {
  254.     return noErr;
  255. }
  256.  
  257. pascal OSErr Ask(AppleEvent* question, AppleEvent* answer)
  258. {
  259.     //    Send the question with an immediate timeout.
  260.  
  261.     OSErr err = AESend(question, answer, kAEWaitReply, kAENormalPriority,
  262.                        kImmediateTimeout, nil, nil);
  263.     
  264.     if (err == errAETimeout)
  265.         err = noErr;
  266.  
  267.     return err;
  268. }
  269.